<!-- eslint-disable no-console -->
<script setup lang="ts">
import { onBeforeUnmount, ref, watch } from 'vue'
import { merge } from 'lodash-es'
import { QuestionCircleOutlined } from '@ant-design/icons-vue'
import { scatterBaseOptions } from '@/components/ui/chart/src/scatterOptions'
import { CHART_COLORS } from '@/components/ui/chart/src/color'
import { hvTransformArray } from '@/utils'

/**
 * 注意点
 * 1. 交互的时候推荐小范围的刷选，刷选左边的大范围数据时会卡顿
 * 2. 切换右边的图例分类时会有点卡顿，那是因为要重新渲染整个图表，请耐心等待片刻
 */

const props = defineProps<{
  loading: boolean
  caseResult: any
}>()

interface scrollYType {
  start: number | null
  end: number | null
  startValue: number | null
  endValue: number | null
}

const legendData = ref<any>([])
const lineDatalist = ref()
const category = ref<any>([])
const scatterData = ref<any[]>([])
const scatterRawData = ref<any[]>([])
const candleStickData = ref<any[]>([])

const scatterTestOptions = ref(scatterBaseOptions) as any
const scrollIndex = ref({ start: 0, end: 20 })
const scrollY = ref<scrollYType>({ start: 0, end: 100, startValue: null, endValue: null })
const selectedPieceseArr = ref()
const hideAllPieceseArr = ref(false)
const hasLineData = ref(false)
const visualMapCatorogyBottom = ref(30)

onBeforeUnmount(() => {
  legendData.value = null as any
  lineDatalist.value = null as any
  category.value = null as any
  scatterData.value = null as any
  scatterRawData.value = null as any
  candleStickData.value = null as any
  scatterTestOptions.value = null as any
  hasLineData.value = null as any
  hideAllPieceseArr.value = null as any
})

watch(() => props.caseResult, async (nVal: any) => {
  if (nVal) {
    await handleCaseResultData()
    initData(candleStickData.value)
  }
})

async function handleCaseResultData() {
  const { columns, data } = props.caseResult.raw_cluster2d['dataset-split']
  legendData.value = columns.slice(0, columns.length - 3)
  scatterRawData.value = data
  // 根据数据量的大小去控制显示多少区域（8% 20% 50% 100%）
  scrollIndex.value.end = data.length > 2500
    ? 8
    : data.length > 1000
      ? 20
      : data.length > 500 ? 50 : 100

  const categoryDataTemp: any[] = []
  const candleStickDataTemp: any = []
  const scatterDataTemp: any = []
  scatterRawData.value.forEach((item: any) => {
    const categoryItem = item[item.length - 1]
    const candleStickItem = item.slice(0, columns.length - 3)
    const scatterItem = item.slice(item.length - 3)
    categoryDataTemp.push(categoryItem)
    candleStickDataTemp.push(candleStickItem)
    // 需要把Noise类的数据放在最前面，scatter图形上的数据才回暂时
    if (categoryItem === 'Noise') {
      scatterDataTemp.unshift(scatterItem)
    }
    else {
      scatterDataTemp.push(scatterItem)
    }
  })

  const uniqueCategoryData = [...new Set(categoryDataTemp)]
  let totalCategory = uniqueCategoryData
  // 把nosie放在最最后面
  if (uniqueCategoryData.includes('Noise')) {
    const otherCategory = uniqueCategoryData.filter(item => item !== 'Noise')
    totalCategory = [...otherCategory, 'Noise']
  }

  category.value = totalCategory.sort((a: any, b: any) => a - b)
  visualMapCatorogyBottom.value = category.value.length > 10 ? 10 : 30
  candleStickData.value = candleStickDataTemp
  scatterData.value = scatterDataTemp
}

function initData(data: any) {
  const transformedData = hvTransformArray(data)
  lineDatalist.value = transformedData.map((item: any, index: number) => {
    return {
      name: hideAllPieceseArr.value ? '' : legendData.value[index],
      symbolSize: 5,
      symbol: 'circle',
      type: 'line',
      data: hideAllPieceseArr.value ? [] : item,
      xAxisIndex: 1,
      yAxisIndex: 1,
      lineStyle: {
        color: '#ccc',
      },
    }
  })
  hasLineData.value = lineDatalist.value.length > 0
  _setChartOption()
}

// 点击切换line图分类
const showAllLine = ref(true)
function onClickToggleAllLine() {
  const selected: Record<string, boolean> = {}
  for (const item of legendData.value) {
    if (showAllLine.value)
      selected[item] = false
    else
      selected[item] = true
  }
  showAllLine.value = !showAllLine.value
  // 修改了scrollY需要重新渲染
  scrollY.value = {
    start: 0,
    end: 100,
    startValue: null,
    endValue: null,
  }
  _setChartOption()
  scatterTestOptions.value.legend.selected = selected
}

function avg(arr: number[]) {
  return arr.reduce((total, value) => total + Number(value), 0) / arr.length
}

function onClickLineLegend(params: any) {
  // 多个分类显示情况下Y轴需要铺满，不然当分类的数据差别很大时平均值会拉到一个都没有很好显示的地方
  const selectedLength = Object.values(params?.selected).filter(Boolean).length
  if (selectedLength > 1) {
    // 每次最好选中一个分类查看趋势，多个分类同时查看趋势时数据会被拉到一个平均值不利于展示
    // 修改了scrollY需要重新渲染
    scrollY.value = {
      start: 0,
      end: 100,
      startValue: null,
      endValue: null,
    }
    _setChartOption()
    return
  }

  for (const key in params?.selected) {
    if (params?.selected[key]) {
      const index = legendData.value.indexOf(key)
      const curData = lineDatalist.value[index].data

      // 偏移量越多就越需要舍弃，使用平均值代替，从而拉开图表趋势
      const deltaMin = Math.abs(Math.min(...curData) - avg(curData))
      const deltaMax = Math.abs(Math.max(...curData) - avg(curData))
      if (deltaMax <= deltaMin) {
        scrollY.value = {
          start: null,
          end: null,
          // 这里的0.01 是为了解决平均值和最大值差距太小导致折线图无法显示或压成了一条线
          startValue: Math.floor(avg(curData) - avg(curData) * 0.01),
          endValue: Math.floor(Math.max(...curData)),
        }
      }
      else {
        scrollY.value = {
          start: null,
          end: null,
          startValue: Math.floor(Math.min(...curData)),
          endValue: Math.floor(avg(curData) + avg(curData) * 0.01),
        }
      }

      // 修改了scrollY需要重新渲染
      _setChartOption()
      return
    }
  }
}

// 点击切换scatter图分类
function onClickPiecewise(data: any) {
  console.time('init')
  selectedPieceseArr.value = data.selected
  const selectedLegendArr = [] as any
  for (const item in data.selected)
    data.selected[item] && selectedLegendArr.push(item)

  // 取消所有顶部piecese选项时下面的lineDatalist要隐藏
  hideAllPieceseArr.value = selectedLegendArr.length === 0
  // 没有选中的点需要用undefined填充, 不然它会自动往前占位导致line变短
  const dynamicLineData = scatterRawData.value.map((item: any) => {
    if (selectedLegendArr.includes(item[item.length - 1]))
      return item.slice(0, item.length - 3)
    else
      return Array(item.length).fill(undefined).slice(0, item.length - 3)
  })

  initData(dynamicLineData)
  console.timeEnd('init')
}

// 回填slider位置
function setSliderPostion(data: any) {
  scrollIndex.value = data
}

// 设置图表配置项
function _setChartOption() {
  scatterTestOptions.value = merge(
    {},
    scatterBaseOptions,
    {
      animation: false, // 交互时禁止动画
      toolbox: {
        left: 'center',
        feature: {
          // dataZoom: {
          //   yAxisIndex: false,
          // },
          brush: {
            type: ['rect', 'lineX', 'clear'],
          },
        },
      },
      dataZoom: [
        {
          type: 'slider', // 缩放条
          xAxisIndex: 1, // 对应第一个x轴
          start: scrollIndex.value.start, // 起始位置百分比
          end: scrollIndex.value.end, // 结束位置百分比
          // startValue: 0, // 起始位置数值
          // endValue: 300, // 结束位置数值
          bottom: 40,
        },
        {
          type: 'inside', // 内置缩放
          xAxisIndex: 1,
          start: scrollIndex.value.start, // 起始位置百分比
          end: scrollIndex.value.end, // 结束位置百分比
        },
        {
          type: 'slider', // 缩放条
          yAxisIndex: 1, // 对应第一个y轴
          start: scrollY.value.start, // 起始位置百分比
          end: scrollY.value.end, // 结束位置百分比
          startValue: scrollY.value.startValue,
          endValue: scrollY.value.endValue,
          left: '55%',
        },
        {
          type: 'inside', // 内置缩放
          yAxisIndex: 1,
          start: scrollY.value.start, // 起始位置百分比
          end: scrollY.value.end, // 结束位置百分比
          startValue: scrollY.value.startValue,
          endValue: scrollY.value.endValue,
        },
      ],
      brush: {
        xAxisIndex: 'all',
        brushLink: 'all',
        inBrush: {
          opacity: 1,
        },
      },
      grid: [
        {
          width: '38%',
          right: '2%',
          top: 140,
          show: true, // border生效需要把show开启
          borderWidth: 1, // 设置边框线宽度
          borderColor: '#6e7079', // 设置边框线颜色
        },
        {
          width: '50%',
          left: '5%',
          top: 140,
          show: true,
          borderWidth: 1,
          borderColor: '#6e7079',
        },
      ],
      xAxis: [
        {
          type: 'value',
          gridIndex: 0,
          axisLine: { show: false }, // 显示横坐标轴线
          // splitLine: { show: false }, // 显示分割线
        },
        {
          type: 'category',
          gridIndex: 1,
          axisLabel: { interval: 0 }, // 设置横坐标全部显示，不间隔
          show: false,
        },
      ],
      yAxis: [
        {
          type: 'value',
          gridIndex: 0,
          splitLine: { show: false },
          boundaryGap: ['0.2', '0.2'],
          axisLine: { show: false },
          // scale: true, // 自适应
          // min: 'dataMin', // 取最小值为最小刻度
          // max: 'dataMax', // 取最大值为最大刻度
          // min: 0,
        },
        {
          type: 'value',
          gridIndex: 1,
          splitLine: { show: false },
          boundaryGap: ['0.2', '0.2'],
          // min: 0,
        },
      ],
      visualMap: {
        seriesIndex: 0,
        type: 'piecewise',
        categories: category.value,
        selected: selectedPieceseArr.value,
        dimension: 2,
        orient: 'horizontal',
        right: '1.5%',
        bottom: visualMapCatorogyBottom.value,
        hoverLink: false,
        inRange: {
          color: CHART_COLORS,
        },
        outRange: {
          color: '#ddd',
        },
      },
      legend: {
        left: 10,
        top: 40,
        // orient: 'vertical',
        data: legendData.value,
      },
      axisPointer: { show: false },
      series: [
        {
          symbolSize: 5,
          type: 'scatter',
          data: scatterData.value,
          xAxisIndex: 0,
          yAxisIndex: 0,
          progressive: 0,
          // large: true,
        },
        ...lineDatalist.value,
        {
          type: 'candlestick',
          color: 'transparent',
          barWidth: 0,
          itemStyle: {
            color: 'transparent',
            color0: 'transparent',
            borderColor: 'transparent',
            borderColor0: 'transparent',
          },
          data: candleStickData.value,
          xAxisIndex: 1,
          yAxisIndex: 1,
        },
      ],
    },
  )
}
</script>

<template>
  <div class="brush-chart-wrapper">
    <div v-if="!hideAllPieceseArr && hasLineData" class="toggle-all-line" @click="onClickToggleAllLine">
      <span class="toggle">ALL</span>
    </div>
    <a-spin tip="loading..." :spinning="props.loading">
      <div
        v-if="scatterTestOptions.series[0].data.length"
        style="position: absolute; top: 5px; right: 44%; z-index: 9999;"
      >
        <a-tooltip>
          <template #title>
            <p>
              1. It is recommended to select a small range when interacting. Selecting a large range of data on the
              left will cause lag
            </p>
            <p>
              2. There will be a little lag when switching the legend category on the right, because the entire chart
              needs to be re-rendered. Please wait patiently for a moment
            </p>
            <p>
              3. It is best to select one category at a time to view trends. When viewing trends in multiple categories
              at the same time, the data will be pulled to an average value, which is not conducive to display.
            </p>
          </template>
          <QuestionCircleOutlined />
        </a-tooltip>
      </div>
      <GpChart
        id="caseBrushScatter" :options="scatterTestOptions" height="500px" @line-click="onClickLineLegend"
        @piecewise-click="onClickPiecewise" @data-zoom-scroll="setSliderPostion"
      />
      <div v-if="!hideAllPieceseArr && hasLineData">
        <span class="samples-name">Sapmles</span>
        <span class="cluster-name">Cluster</span>
        <span class="values">Values</span>
      </div>
    </a-spin>
  </div>
</template>

<style lang="less" scoped>
.brush-chart-wrapper {
  position: relative;

  .toggle-all-line {
    display: inline-block;
    position: absolute;
    left: 10px;
    top: 10px;
    font-size: 14px;
    z-index: 9999;

    .toggle {
      color: #fff;
      padding: 2px 10px;
      border-radius: 4px;
      background-color: @color-theme;
      cursor: pointer;
    }
  }

  .samples-name {
    position: absolute;
    top: 115px;
    left: 25%;
    font-size: 16px;
  }

  .cluster-name {
    position: absolute;
    top: 115px;
    right: 22%;
    font-size: 16px;
  }

  .values {
    position: absolute;
    top: 54%;
    left: 54%;
    font-size: 16px;
    transform: rotate(-90deg);
  }
}
</style>
